package {
import flash.geom.Point;
import org.flixel.*;
	public class Player extends FlxSprite {
        
		// Art for Player
		[Embed(source="player.png")]
		private var playerSprite:Class;
		
		// Art for speech bubble
		[Embed(source = "speech_bubble_background.png")]
		private var speechBack:Class;
		
		[Embed(source = "speech_bubble_left.png")]
		private var speechLeft:Class;
		public const BUBBLE_LEFT:int = 0;
		
		[Embed(source = "speech_bubble_right.png")]
		private var speechRight:Class;
		public const BUBBLE_RIGHT:int = 1;
		
		[Embed(source = "speech_bubble_up.png")]
		private var speechUp:Class;
		public const BUBBLE_UP:int = 2;
		
		[Embed(source = "speech_bubble_shift.png")]
		private var speechShift:Class;
		public const BUBBLE_SHIFT:int = 3;
		
		[Embed(source = "speech_bubble_shift_down.png")]
		private var speechShiftDown:Class;
		public const BUBBLE_SHIFT_DOWN:int = 7;
		
		[Embed(source="speech_bubble_exit.png")]
		private var speechExit:Class;
		public const BUBBLE_EXIT:int = 4;
		
		[Embed(source="speech_bubble_down.png")]
		private var speechDown:Class;
		public const BUBBLE_DOWN:int = 5;
		
		[Embed(source="speech_bubble_switch.png")]
		private var speechSwitch:Class;
		public const BUBBLE_SWITCH:int = 6;
		
		[Embed(source="speech_bubble_ink.png")]
		private var speechInk:Class;
		public const BUBBLE_INK:int = 8;
		
        // Jump states for the player
        public const GROUND:int = 0;
        public const AIR_RIGHT:int = 1;
        public const AIR_LEFT:int = 2;
        public const AIR_NEUTRAL:int = 3;
		// public const SLIDING:int = 5;
		
        // Air status
        public var jumpStatus:int;
        
		// Length of each ink lapse
		private const INK_DURATION:int = 90;
		private const MIN_INK:int = 20;
		public const GRAV_NORM:int = 375;
        	
		/* Fields */
		// Reference to current level
	    private var _level:Level;
		
		// Ink fields
        private var drawingInk:Boolean = false;
		private var pressedShift:Boolean = false;
		private var delayedRelease:Boolean = false;
        private var inkCount:int;
		public var currInkLeft:int;
		
		// Differentiate from a jump to a walk-off-edge for ink platform pleasantness
		private var leapt:Boolean = false;
		
		// Ink platform collision handling
		
		// Each update step, we must reconcile our positon with points in this array.
		// One point per ink platform, which is the average of all points that overlap
		// the player.
		private var inkPlatformCollisionPts:Vector.<FlxPoint>;
		// Along with each point, provide a slope for further handling.
		private var inkPlatformCollisionVects:Vector.<FlxPoint>;
		// Used for collision detection
		private var lastPos:FlxPoint;
		
		// Sprite with a speech bubble asking player to perform a specific 
		// action
		private var speechBG:FlxSprite;
		private var speechIcon:FlxSprite;
		private var speechTimer:Number;
		
		// Set to true to use bitmap-based collision handling
		private const bitmapInk:Boolean = false;
		
		// Use this to allow player to jump just after touching ink
		private var inkJumpTimer:Number = 0.0;
        
		public function Player(X:Number, Y:Number, State:Level, InkMax:int) {
			super(X, Y);
            
			// Reference to level currently on since the player itself can't add anything; only states can add objects
			_level = State;
			
			// Ink given
			inkCount = InkMax;
			
			// Current default settings
			loadGraphic(playerSprite, true, true, 20, 40, true);
			//makeGraphic(10, 20, 0xff000000);
            maxVelocity.x = 160;
            maxVelocity.y = 250;
			acceleration.y = GRAV_NORM;
			
            // Bounding box of player
            width = 20;
            height = 40;
			
			addAnimation("normal", [0]);
			addAnimation("walk", [0, 1, 2], 10);
			addAnimation("jump", [3, 4], 8, false);
			addAnimation("inAir", [4]);
			
            jumpStatus = GROUND;
			inkPlatformCollisionPts = new Vector.<FlxPoint>();
			inkPlatformCollisionVects = new Vector.<FlxPoint>();
			
			lastPos = new FlxPoint();
			
			
			// Speech bubbles
			speechTimer = 0;
			
			speechBG = new FlxSprite(X, Y);
			speechBG.loadGraphic(speechBack);
			speechBG.solid = false;
			
			speechIcon = new FlxSprite(X,Y);
			speechIcon.solid = false;
		}
		
		
		// Draw speech bubbles for hints/tutorial above player
		override public function draw():void {
			super.draw();
			
			if ( speechTimer > 0 )
			{
				speechBG.draw();
				speechIcon.draw();
			}
		}
		
		override public function update():void {
			// Check player's collision with any ink platforms
			if ( speechTimer > 0 )
				speechTimer -= FlxG.elapsed;
			
            // ------------------ Player movement and controls---------------------------                
            if ((FlxG.keys.A || FlxG.keys.LEFT) && jumpStatus == GROUND) {
				if ( facing != RIGHT && !isTouching(LEFT) && velocity.x > -maxVelocity.x )
				{
					if ( velocity.x > 0 )
						velocity.x = velocity.x - 0.2 * maxVelocity.x;
					else
						velocity.x = velocity.x - 0.05 * maxVelocity.x;
					
					if ( velocity.x < -maxVelocity.x )
						velocity.x = -maxVelocity.x;
				}
				facing = LEFT;
			}
            if ((FlxG.keys.D || FlxG.keys.RIGHT) && jumpStatus == GROUND) {
				if ( facing != LEFT && !isTouching(RIGHT) && velocity.x < maxVelocity.x )
				{
					if ( velocity.x < 0 )
						velocity.x = velocity.x + 0.2 * maxVelocity.x;
					else
						velocity.x = velocity.x + 0.05 * maxVelocity.x;
						
					if ( velocity.x > maxVelocity.x )
						velocity.x = maxVelocity.x;
				}
				facing = RIGHT;
			}
            if((!FlxG.keys.D && !FlxG.keys.RIGHT) && (!FlxG.keys.A && !FlxG.keys.LEFT) && jumpStatus == GROUND) {
                velocity.x = 0;
            }
            
            if ((FlxG.keys.W || FlxG.keys.UP) && jumpStatus == GROUND && velocity.x == 0) {
                jumpStatus = AIR_NEUTRAL;
                velocity.y = -maxVelocity.y;
				acceleration.y = GRAV_NORM;
				leapt = true;
			} else if ((FlxG.keys.W || FlxG.keys.UP) && jumpStatus == GROUND && velocity.x > 0) {
                jumpStatus = AIR_RIGHT;
                velocity.y = -maxVelocity.y;
				acceleration.y = GRAV_NORM;
				leapt = true;
			} else if ((FlxG.keys.W || FlxG.keys.UP) && jumpStatus == GROUND && velocity.x < 0) {
                jumpStatus = AIR_LEFT;
                velocity.y = -maxVelocity.y;
				acceleration.y = GRAV_NORM;
				leapt = true;
			}
			
			if ( (FlxG.keys.W || FlxG.keys.UP) && inkJumpTimer > 0.0 && !leapt )
			{
				if ( velocity.x > 0 )
				{
					jumpStatus = AIR_RIGHT;
				}
				else if ( velocity.x < 0 )
				{
					jumpStatus = AIR_LEFT;
				}
				
                velocity.y = -maxVelocity.y;
				acceleration.y = GRAV_NORM;
				leapt = true;
			}
			
			
			// -------------------------- Air physics -----------------------------------    
            
            // Define Air Movements
            // Air movement from 0 x-velocity
            
            if ((FlxG.keys.A || FlxG.keys.LEFT) && jumpStatus == AIR_LEFT) {
				if ( leapt )
					velocity.x = velocity.x - 0.02 * maxVelocity.x;
				else
				{
					velocity.x = velocity.x - 0.005 * maxVelocity.x;
					
					if ( velocity.x < 0.75 * -maxVelocity.x )
						velocity.x = 0.75 * -maxVelocity.x;
				}
					
				if ( velocity.x < (0.75 * -maxVelocity.x) && leapt )
				{
					velocity.x = -maxVelocity.x;
				}
			}
            if ((FlxG.keys.D || FlxG.keys.RIGHT) && jumpStatus == AIR_RIGHT) {
                if ( leapt )
					velocity.x = velocity.x + 0.02 * maxVelocity.x;
				else
				{
					velocity.x = velocity.x + 0.005 * maxVelocity.x;
					
					if ( velocity.x < 0.75 * -maxVelocity.x )
						velocity.x = 0.75 * -maxVelocity.x;
				}
					
				if ( velocity.x > (0.75 * maxVelocity.x) && leapt )
					velocity.x = maxVelocity.x;
			}
            if ((FlxG.keys.A || FlxG.keys.LEFT) && (jumpStatus == AIR_RIGHT || jumpStatus == AIR_NEUTRAL)) {
                velocity.x = -0.02 * maxVelocity.x;
                jumpStatus = AIR_LEFT;
				//facing = LEFT;
			}
            if ((FlxG.keys.D || FlxG.keys.RIGHT) && (jumpStatus == AIR_LEFT || jumpStatus == AIR_NEUTRAL)) {
                velocity.x = 0.02 * maxVelocity.x;
				//facing = RIGHT;
                jumpStatus = AIR_RIGHT;
			}
            
            // -------------------------- Ink physics -----------------------------------
			// Activate Ink ability reseting the current ink left counter
            
			if ( TitleScreen.toggleShift )
			{
	            if (FlxG.keys.justPressed("SHIFT") && !drawingInk && inkCount > 0) {
					drawingInk = true;
					currInkLeft = INK_DURATION;
					// Decrease total ink count
					inkCount--;
				}
				
				if (FlxG.keys.justReleased("SHIFT") && drawingInk) {
					pressedShift = true;
				}
				
				// Draw ink behind player
				if (drawingInk) {
					if (facing == RIGHT) {
						_level.addInk(this.x - 1, this.y + 41);
					} else if (facing == LEFT) {
						_level.addInk(this.x + 21, this.y + 41);
					}
					// Decrease amount of ink left for this current drawing
					currInkLeft--;
					// When run out, stop drawing ink
					if (currInkLeft == 0 || FlxG.keys.SHIFT && pressedShift) {
						_level.finishInk();
						drawingInk = false;
						pressedShift = false;
					}
				}
			}
			else
			{
				if (FlxG.keys.justPressed("SHIFT") && inkCount > 0 && !pressedShift) {
					if (!drawingInk) {
						pressedShift = true;
						drawingInk = true;
						currInkLeft = INK_DURATION;
						// Decrease total ink count
						inkCount--;
					}
				}
				if (drawingInk && FlxG.keys.justReleased("SHIFT")) {
					if ( currInkLeft < INK_DURATION - MIN_INK )
					{
						drawingInk = false;
						pressedShift = false;
						delayedRelease = false;
						_level.finishInk();
					}
					else
					{
						delayedRelease = true;
					}
				}
				
				// If we released shift but before the min duration
				if ( delayedRelease && currInkLeft < INK_DURATION - MIN_INK )
				{
					drawingInk = false;
					pressedShift = false;
					_level.finishInk();
					delayedRelease = false;
				}
				
				// FlxG.log("Current ink left: " + currInkLeft);
				if (drawingInk && pressedShift) {
					if (facing == RIGHT) {
						_level.addInk(this.x - 1, this.y + 41);
					} else if (facing == LEFT) {
						_level.addInk(this.x + 21, this.y + 41);
					}
					// Decrease amount of ink left for this current drawing
					currInkLeft--;
					// When run out, stop drawing ink
					if (currInkLeft == 0) {
						_level.finishInk();
						drawingInk = false;
						pressedShift = false;
					}
				}
			}

			// Draw ink behind player

            // Setting Air flag: 
            if (isTouching(FlxObject.DOWN)) {
                jumpStatus = GROUND;
				leapt = false;
            } else {
				if (velocity.x == 0) {
					jumpStatus = AIR_NEUTRAL;
				} else if (velocity.x > 0) {
					jumpStatus = AIR_RIGHT;
				} else {
					jumpStatus = AIR_LEFT;
				}
			}
            
            //Updates all the objects appropriately
			
            // -------------------------- Animations -----------------------------------
			if ((FlxG.keys.W || FlxG.keys.UP) && jumpStatus == GROUND) {
				play("jump", true);
			} else if (velocity.x != 0 && (jumpStatus == GROUND)) {
				play("walk");
			} else if (velocity.x == 0 && jumpStatus == GROUND){
				play("normal");
			} else if (jumpStatus == AIR_LEFT && velocity.y >= 0) {
				play("inAir");
			}

				
			lastPos.x = super.x;
			lastPos.y = super.y;
			
			speechBG.x = super.x - 50;
			speechBG.y = super.y - 80;
			
			speechIcon.x = super.x - 10;
			speechIcon.y = super.y - 62;
			
			if ( inkJumpTimer > 0.0 )
				inkJumpTimer -= FlxG.elapsed;
			else
				inkJumpTimer = 0.0;
			
			super.update();
        }
		
		public function getInkCount():uint {
			return inkCount;
		}
		
		public function updateInkCollisions():void {
			if ( inkPlatformCollisionPts.length != 0 )
			{
				// We had a collision.  Get our delta.
				var deltaX:Number = super.x - lastPos.x;
				var deltaY:Number = super.y - lastPos.y;
				
				// For each collision...
				while(inkPlatformCollisionPts.length > 0)
				{	
					var topLeft:FlxPoint = new FlxPoint(super.x, super.y);
					var topRight:FlxPoint = new FlxPoint(super.x + super.width, super.y);
					var botRight:FlxPoint = new FlxPoint(super.x + super.width, super.y + super.height);
					var botLeft:FlxPoint = new FlxPoint(super.x, super.y + super.height);
					
					var curPt:FlxPoint = inkPlatformCollisionPts.pop();
					var curNorm:FlxPoint = inkPlatformCollisionVects.pop();
					
					// Find the closest corner to our composite point
					var shortest:Number = FlxU.getDistance(topLeft, curPt);
					var cornerIdx:int = 0; // 0 = top-left, clockwise from there
					
					if ( FlxU.getDistance(topRight, curPt) < shortest )
					{
						shortest = FlxU.getDistance(topRight, curPt);
						cornerIdx = 1;
					}
					
					if ( FlxU.getDistance(botRight, curPt) < shortest )
					{
						shortest = FlxU.getDistance(botRight,curPt);
						cornerIdx = 2;
					}
					
					if ( FlxU.getDistance(botLeft, curPt) < shortest )
					{
						shortest = FlxU.getDistance(botLeft, curPt);
						cornerIdx = 3;
					}
					
					// Move back 
					super.velocity.x = 0;
					super.velocity.y = 0;
					
					// Build a unit vector that is perpendicular to slope to move back along
					var uv:FlxPoint = new FlxPoint( -curNorm.y, curNorm.x);
						
					var dist:Number = FlxU.getDistance(uv, new FlxPoint(0, 0));
					uv.x /= dist; uv.y /= dist;
					uv.x *= shortest; uv.y *= shortest;
					
					if ( uv.x * deltaX + uv.y * deltaY > 0 ) {
						super.x = super.x - uv.x; super.y = super.y - uv.y;
						//if ( uv.y / uv.x < 1 )
							//jumpStatus = INK;
					}else{
						super.x = super.x + uv.x; super.y = super.y + uv.y;
					}
				}
			}
		}
		
		public function inkCollision(point:FlxPoint, normal:FlxPoint) : void
		{
			inkPlatformCollisionPts.push(point);
			inkPlatformCollisionVects.push(normal);
		}
		
		public function setSpeech(graphic:int, seconds:int) : void
		{
			speechTimer = seconds;
			
			if ( graphic == BUBBLE_LEFT )
				speechIcon.loadGraphic(speechLeft);
			else if ( graphic == BUBBLE_RIGHT )
				speechIcon.loadGraphic(speechRight);
			else if ( graphic == BUBBLE_UP )
				speechIcon.loadGraphic(speechUp);
			else if ( graphic == BUBBLE_SHIFT )
				speechIcon.loadGraphic(speechShift);
			else if ( graphic == BUBBLE_SHIFT_DOWN )
				speechIcon.loadGraphic(speechShiftDown);
			else if ( graphic == BUBBLE_EXIT )
				speechIcon.loadGraphic(speechExit);
			else if ( graphic == BUBBLE_DOWN )
				speechIcon.loadGraphic(speechDown);
			else if ( graphic == BUBBLE_SWITCH )
				speechIcon.loadGraphic(speechSwitch);
			else if ( graphic == BUBBLE_INK )
				speechIcon.loadGraphic(speechInk);
			else
				speechIcon.loadGraphic(null);
		}
		
		public function isSpeechUp() : Boolean
		{
			return speechTimer > 0;
		}
		
		public function clearSpeech() : void
		{
			speechTimer = 0;
		}
		
		public function enableInkJump() : void
		{
			inkJumpTimer = 0.2;
		}
	}
}